package my.org.swing.animate;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.util.ArrayList;
import java.util.List;
import javax.swing.CellRendererPane;
import javax.swing.JPanel;
import org.jdesktop.core.animation.timing.Animator;
import org.jdesktop.core.animation.timing.TimingTarget;
import org.jdesktop.core.animation.timing.TimingTargetAdapter;

public class AnimatePane extends JPanel {

    private static final long serialVersionUID = 2722474171850109405L;

    private CellRendererPane crp = new CellRendererPane();

    private List<Component> children = new ArrayList<>();

    private Component current;

    private Component next;

    private volatile double ratio;

    private Transformer policy = new TranslateActor(TranslateActor.CenterExpand);

    private MyAnimator animator;

    public AnimatePane() {
        this(500);
    }

    public AnimatePane(int duration) {
        this(duration, null);
    }

    public AnimatePane(int duration, TimingTarget listener) {
        this.setLayout(new Layout());
        super.add(crp);

        animator = new MyAnimator(duration, new TimingTargetAdapter() {

            @Override
            public void begin(Animator source) {
                current.setVisible(false);
                next.setVisible(false);
                if (listener != null) {
                    listener.begin(source);
                }
            }

            @Override
            public void end(Animator source) {
                setContent(next);
                next.setVisible(true);
//                doLayout();
                if (listener != null) {
                    listener.end(source);
                }
            }

            @Override
            public void timingEvent(Animator source, double fraction) {
                ratio = fraction;
                repaint();
                if (listener != null) {
                    listener.timingEvent(source, fraction);
                }
            }
        });
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (policy == null || current == null || next == null || !animator.isRunning()) {
            return;
        }

        int width = getWidth();
        int height = getHeight();
        g.setColor(current.getBackground());// 背景色统一
        g.fillRect(0, 0, width, height);

        Graphics2D g1 = (Graphics2D) g.create();
        Graphics2D g2 = (Graphics2D) g.create();
        policy.transform(g1, g2, ratio, width, height);
        crp.paintComponent(g1, current, this, 0, 0, width, height, true);
        crp.paintComponent(g2, next, this, 0, 0, width, height, true);

        g1.dispose();
        g2.dispose();
    }

    public Component addElement(Component c) {
        return add(c);
    }

    public void removeElement(Component c) {
        super.remove(c);
        children.remove(c);
    }

    @Override
    public Component add(Component c) {
        if (c == null) {
            throw new IllegalArgumentException("component is null");
        }

        if (current == c) {
            return c;
        }

        if (current == null) {
            current = c;
            super.add(c);
        }

        children.add(c);
        return c;
    }

    public void setContent(Component c) {
        if (current != null) {
            remove(current);
            children.remove(current);
            current = null;
        }

        add(c);
    }

    @Override
    public void remove(int index) {
        if (index <= 0) {
            return;
        }

        super.remove(index);
    }

    public void show(Component c) {
        if (c == null || current == c) {
            return;
        }

        next = c;
        if (animator.isRunning()) {
            animator.stop();
            return;
        }

        animator.start();
    }

    public void showNext() {
        if (children.size() < 2) {
            return;
        }

        int i = children.indexOf(current);
        Component c;
        if (i >= children.size() - 1) {
            c = children.get(0);
        } else {
            c = children.get(i + 1);
        }

        show(c);
    }

    public boolean isCurrentShow(Component c) {
        return current == c;
    }

    public void setPolicy(Transformer policy) {
        this.policy = policy;
    }

    class Layout implements LayoutManager {

        @Override
        public void addLayoutComponent(String name, Component comp) {

        }

        @Override
        public void removeLayoutComponent(Component comp) {

        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            return current != null ? current.getPreferredSize() : new Dimension(0, 0);
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return preferredLayoutSize(parent);
        }

        @Override
        public void layoutContainer(Container parent) {
            synchronized (parent.getTreeLock()) {
                Insets insets = parent.getInsets();
                int x = insets.left;
                int y = insets.top;
                int width = parent.getWidth() - insets.left - insets.right;
                int height = parent.getHeight() - insets.top - insets.bottom;

                if (current != null && current.isVisible()) {
                    current.setBounds(x, y, width, height);
                }
            }
        }
    }
}
